/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package simulator.services;

import simulator.statistics.Utility;

/**
 *
 * @author Eran
 */
public class Clock {

    /**
     * time[0] = hours, time[1] = minutes, time[2] = seconds
     */
    private int[] time;
    public static final int[] cicle = {24, 60, 60};

    private static final int tickConversion = Utility.TICK_CONVERSION;

    public Clock() {
        this.time = new int[]{0, 0, 0};
    }

    /**
     * assuming the input will always be a prefix of [hour, minute, second].
     *
     * @param time
     */
    public Clock(int... time) {
        this.time = new int[]{0, 0, 0};
        System.arraycopy(time, 0, this.time, 0, time.length);
    }

    public void tick() {
        int carry = 1;
        for (int i = Clock.tickConversion; i >= 0; i--) {
            if (carry == 0) {
                return;
            }
            carry = (time[i] + 1) / cicle[i];
            time[i] = (time[i] + 1) % cicle[i];
        }
    }

    /**
     * Reset time at clock to 00:00:00.
     */
    public void reset() {
        this.time[0] = this.time[1] = this.time[2] = 0;
    }

    /**
     * Get the current time by a number of ticks.
     * @return 
     */
    public int getTicks() {
        switch (Clock.tickConversion) {
            case 0: // tick is interpreted as an hour.
                return time[0];
            case 1: // tick is interpreted as an minute.
                return time[0] * 60 + time[1];
            case 2: // tick is interpreted as an second.
                return (time[0] * 60 + time[1]) * 60 + time[2];
            default:
                throw new UnsupportedOperationException("Unknown value for tick-conversion.");
        }
    }

    /**
     * Compare the time in the clocks. Return 0 if equal, 1 if current clock
     * preceed the other one and -1 if the other clock preceed the current one.
     *
     * @param other
     * @return
     */
    public int compare(Clock other) {
        return this.compare(other.time);
    }

    /**
     * Compare the time in the clocks. Return 0 if equal, 1 if current clock
     * comes after the other one and -1 if the other clock comes after the current one.
     *
     * @param other
     * @return
     */
    public int compare(int[] other) {
        for (int i = 0; i < time.length; i++) {
            if (time[i] != other[i]) {
                return (int) Math.signum(time[i] - other[i]);
            }
        }
        return 0;
    }

    /**
     * Compare the time [h:m:s] to the time in the current clock. Return 0 if
     * equal, 1 if current clock preceed the other one and -1 if the other clock
     * preceed the current one.
     *
     * @param h
     * @param m
     * @param s
     * @return
     */
    public int compare(int h, int m, int s) {
        int[] d = {time[0] - h, time[1] - m, time[2] - s};
        for (int i = 0; i < d.length; i++) {
            if (d[i] != 0) {
                return (int) Math.signum(d[i]);
            }
        }
        return 0;
    }

    /**
     * Set the clock to the given time.
     * @param time 
     */
    public void set(int... time) {
        int toCopy = Math.min(time.length, Clock.tickConversion + 1);
        System.arraycopy(time, 0, this.time, 0, toCopy);
    }

    @Override
    public String toString() {
        return String.format("%02d:%02d:%02d", time[0], time[1], time[2]);
    }

    public int[] toArray() {
        return new int[]{time[0], time[1], time[2]};
    }

    /**
     * Input should be in the pattern: 'hh:mm:ss'.
     * @param str
     * @return 
     */
    public static int[] parseStringClock(String str) {
        int h = Integer.parseInt(str.substring(0, 2));
        int m = Integer.parseInt(str.substring(3, 5));
        int s = Integer.parseInt(str.substring(6, 8));
        return new int[]{h,m,s};
    }
    

    /**
     * Subtract two clocks (represented as arrays), and return the result.
     * Returned value is (clock2 - clock1).
     *
     * @param clock1
     * @param clock2
     * @return
     */
    public static int[] subtractClocks(int[] clock1, int[] clock2) {
        int c = 0;
        int[] sub = {0, 0, 0};
        for (int i = sub.length - 1; i >= 0; i--) {
            int d = clock2[i] - c - clock1[i];
            sub[i] = (d < 0) ? d + Clock.cicle[i] : d;
            c = (d < 0) ? 1 : 0;
        }
        return sub;
    }

    /**
     * Add two clocks (represented as arrays), and return the result.
     * Returned value is (clock1 + clock2).
     *
     * @param clock1
     * @param clock2
     * @return
     */
    public static int[] addClocks(int[] clock1, int[] clock2) {
        int c = 0;
        int[] sum = {0, 0, 0};
        for (int i = sum.length - 1; i >= 0; i--) {
            int d = clock1[i] + clock2[i] + c;
            sum[i] = (d >= Clock.cicle[i]) ? d - Clock.cicle[i] : d;
            c = (d >= Clock.cicle[i]) ? 1 : 0;
        }
        return sum;
    }
    
    public static Clock generateRandomClock(int[] from, int[] to){
        int[] min = new int[3], max = new int[3], diff = new int[3], randDiff = new int[3];
        System.arraycopy(from, 0, min, 0, from.length);
        System.arraycopy(to, 0, max, 0, to.length);
        System.arraycopy(subtractClocks(min, max), 0, diff, 0, tickConversion + 1);
        
        int randDiffTicks = Utility.rand.nextInt(diff[0]*3600 + diff[1]*60 + diff[2]);
        for (int i = 2; i >= 0; i--) {
            if(i <= tickConversion)
                randDiff[i] = randDiffTicks % 60;
            randDiffTicks /= 60;
        }
        
        Clock ans = new Clock(addClocks(min, randDiff));
//        System.out.printf("min = %d:%d:%d\nmax = %d:%d:%d\nresult = %s\n", min[0],min[1],min[2],max[0],max[1],max[02],ans );
        if(ans.compare(min) < 0 || ans.compare(max) > 0)
            throw new UnsupportedOperationException("Random clock not in range (probably an implementation problem).");
        return ans;
    }

}
